import { getUUID } from "./utils";
export default class ImageMark {
  el = null;
  ctx1 = null;
  width = 100; //图片和画布宽高
  height = 100;
  imgUrl = null; //图片url
  rects = []; //边框数组
  pickRect = null; //当前选中的边框
  color = "#f00"; //边框颜色
  scolor = "#f00"; //选中颜色
  moveColor = "#a60909"; //移动颜色
  lineWidth = 1; //边框宽度
  event = {}; //事件
  readonly = true; //是否只读
  swidth = 8; //拖动小矩形边框宽度
  constructor(options) {
    if (typeof options != "object") {
      throw new Error("options must be an object");
    }

    for (const key in options) {
      this[key] = options[key];
    }

    this.init();
  }
  init() {
    let that = this;
    this.ctx1 = this.el.getContext("2d");
    let img = new Image();
    img.src = this.imgUrl;
    img.onload = () => {
      draw();
    };

    let scale = 1;
    let scaleX = 0;
    let scaleY = 0;
    let translateX = 0;
    let translateY = 0;

    let drawingRect = null;
    let startX = 0;
    let startY = 0;
    let editRect = null;
    let shiftX = 0;
    let shiftY = 0;
    let draggingRect = null;
    let startEditRect = null;
    let dragingEditor = null;
    let editorShiftX = 0;
    let editorShiftY = 0;
    let rotatingRect = null;

    let prevX = 0;
    let prevY = 0;

    function computexy(x, y) {
      const xy = {
        // x: x / scale - translateX,
        // y: y / scale - translateY,
        x: (x - scaleX * (1 - scale) - translateX * scale) / scale,
        y: (y - scaleY * (1 - scale) - translateY * scale) / scale,
      };
      return xy;
    }
    function computewh(width, height) {
      return {
        width: width / scale,
        height: height / scale,
      };
    }
    function computeRect(rect) {
      const cr = {
        ...computexy(rect.x, rect.y),
        ...computewh(rect.width, rect.height),
        rotatable: rect.rotatable,
        rotateAngle: rect.rotateAngle,
      };
      return cr;
    }
    function poInRect({ x, y }, rect) {
      return (
        x >= rect.x &&
        x <= rect.x + rect.width &&
        y >= rect.y &&
        y <= rect.y + rect.height
      );
    }
    function getRelativeRotationAngle(point, prev, center) {
      // 计算上一次鼠标位置和旋转中心的角度
      let prevAngle = Math.atan2(prev.y - center.y, prev.x - center.x);

      // 计算当前鼠标位置和旋转中心的角度
      let curAngle = Math.atan2(point.y - center.y, point.x - center.x);

      // 得到相对旋转角度
      let relativeAngle = curAngle - prevAngle;

      return relativeAngle;
    }
    // 将点绕 rotateCenter 旋转 rotateAngle 度
    function rotatePoint(point, rotateCenter, rotateAngle) {
      let dx = point.x - rotateCenter.x;
      let dy = point.y - rotateCenter.y;

      let rotatedX =
        dx * Math.cos((-rotateAngle * Math.PI) / 180) -
        dy * Math.sin((-rotateAngle * Math.PI) / 180) +
        rotateCenter.x;
      let rotatedY =
        dy * Math.cos((-rotateAngle * Math.PI) / 180) +
        dx * Math.sin((-rotateAngle * Math.PI) / 180) +
        rotateCenter.y;

      return { x: rotatedX, y: rotatedY };
    }
    function poInRotRect(
      point,
      rect,
      rotateCenter = {
        x: rect.x + rect.width / 2,
        y: rect.y + rect.height / 2,
      },
      rotateAngle = rect.rotateAngle
    ) {
      if (rotateAngle) {
        const rotatedPoint = rotatePoint(point, rotateCenter, rotateAngle);
        const res = poInRect(rotatedPoint, rect);
        return res;
      }
      return poInRect(point, rect);
    }

    function computeEditRect(rect) {
      let width = that.swidth;
      let linelen = 16;
      return {
        ...(rect.rotatable
          ? {
              rotr: {
                type: "rotr",
                x: rect.x + rect.width / 2 - width / 2,
                y: rect.y - width / 2 - linelen - width,
                width,
                height: width,
              },
              rotl: {
                type: "rotl",
                x1: rect.x + rect.width / 2,
                y1: rect.y - linelen - width / 2,
                x2: rect.x + rect.width / 2,
                y2: rect.y - width / 2,
              },
            }
          : null),
        t: {
          type: "t",
          x: rect.x + rect.width / 2 - width / 2,
          y: rect.y - width / 2,
          width,
          height: width,
          cursor: "n-resize",
        },
        b: {
          type: "b",
          x: rect.x + rect.width / 2 - width / 2,
          y: rect.y + rect.height - width / 2,
          width,
          height: width,
          cursor: "n-resize",
        },
        l: {
          type: "l",
          x: rect.x - width / 2,
          y: rect.y + rect.height / 2 - width / 2,
          width,
          height: width,
          cursor: "e-resize",
        },
        r: {
          type: "r",
          x: rect.x + rect.width - width / 2,
          y: rect.y + rect.height / 2 - width / 2,
          width,
          height: width,
          cursor: "e-resize",
        },
        tl: {
          type: "tl",
          x: rect.x - width / 2,
          y: rect.y - width / 2,
          width,
          height: width,
          cursor: "nw-resize",
        },
        tr: {
          type: "tr",
          x: rect.x + rect.width - width / 2,
          y: rect.y - width / 2,
          width,
          height: width,
          cursor: "ne-resize",
        },
        bl: {
          type: "bl",
          x: rect.x - width / 2,
          y: rect.y + rect.height - width / 2,
          width,
          height: width,
          cursor: "sw-resize",
        },
        br: {
          type: "br",
          x: rect.x + rect.width - width / 2,
          y: rect.y + rect.height - width / 2,
          width,
          height: width,
          cursor: "se-resize",
        },
      };
    }

    function poInEditor(point, rect) {
      const editor = computeEditRect(rect);
      if (!editor) return;

      for (const edit of Object.values(editor)) {
        if (
          poInRotRect(
            point,
            edit,
            {
              x: rect.x + rect.width / 2,
              y: rect.y + rect.height / 2,
            },
            rect.rotateAngle
          )
        ) {
          return edit;
        }
      }
    }

    this.el.onmousedown = (e) => {
      if (this.readonly) return;
      startX = e.offsetX;
      startY = e.offsetY;
      const { x, y } = computexy(e.offsetX, e.offsetY);

      if (editRect) {
        const editor = poInEditor({ x, y }, editRect);
        if (editor) {
          // e.target.style.cursor = "move";
          // 调整旋转
          if (editor.type === "rotr") {
            e.target.style.cursor = "grab";
            rotatingRect = editRect;
            prevX = e.offsetX;
            prevY = e.offsetY;
            return;
          }
          // 调整大小
          startEditRect = { ...editRect };
          dragingEditor = editor;
          editorShiftX = x - editor.x;
          editorShiftY = y - editor.y;
          return;
        }
      }

      this.pickRect = this.rects.find((r) => {
        return poInRotRect({ x, y }, r);
      });

      if (this.pickRect) {
        if (editRect && this.pickRect !== editRect) {
          // 选择了其他矩形
          editRect.isEditing = false;
          editRect = null;
        }
        shiftX = x - this.pickRect.x;
        shiftY = y - this.pickRect.y;
        this.pickRect.isEditing = true;
        editRect = this.pickRect;
        draggingRect = this.pickRect;
        draw();
      } else {
        if (editRect) {
          editRect.isEditing = false;
          editRect = null;
          draw();
        }
        drawingRect = drawingRect || {};
      }
    };

    this.el.onmousemove = (e) => {
      if (this.readonly) return;
      // 绘制中
      if (drawingRect) {
        drawingRect = computeRect({
          x: startX,
          y: startY,
          width: e.offsetX - startX,
          height: e.offsetY - startY,
        });
        draw();
        return;
      }
      const { x, y } = computexy(e.offsetX, e.offsetY);

      // 当前正在拖拽矩形
      if (draggingRect) {
        e.target.style.cursor = "move";
        draggingRect.x = x - shiftX;
        draggingRect.y = y - shiftY;
        draw();
        return;
      }

      // 如果存在编辑中的元素
      if (editRect) {
        const editor = poInEditor({ x, y }, editRect);

        // 旋转中
        if (rotatingRect) {
          const relativeAngle = getRelativeRotationAngle(
            computexy(e.offsetX, e.offsetY),
            computexy(prevX, prevY),
            {
              x: editRect.x + editRect.width / 2,
              y: editRect.y + editRect.height / 2,
            }
          );
          editRect.rotateAngle += (relativeAngle * 180) / Math.PI;
          prevX = e.offsetX;
          prevY = e.offsetY;
          draw();
          return;
        }

        // 调整大小中
        if (dragingEditor) {
          e.target.style.cursor = dragingEditor.cursor;
          const moveX = (e.offsetX - startX) / scale;
          const moveY = (e.offsetY - startY) / scale;

          switch (dragingEditor.type) {
            case "t":
              editRect.y = startEditRect.y + moveY;
              editRect.height = startEditRect.height - moveY;
              break;
            case "b":
              editRect.height = startEditRect.height + moveY;
              break;
            case "l":
              editRect.x = startEditRect.x + moveX;
              editRect.width = startEditRect.width - moveX;
              break;
            case "r":
              editRect.width = startEditRect.width + moveX;
              break;
            case "tl":
              editRect.x = startEditRect.x + moveX;
              editRect.y = startEditRect.y + moveY;
              editRect.width = startEditRect.width - moveX;
              editRect.height = startEditRect.height - moveY;
              break;
            case "tr":
              editRect.y = startEditRect.y + moveY;
              editRect.width = startEditRect.width + moveX;
              editRect.height = startEditRect.height - moveY;
              break;
            case "bl":
              editRect.x = startEditRect.x + moveX;
              editRect.width = startEditRect.width - moveX;
              editRect.height = startEditRect.height + moveY;
              break;
            case "br":
              editRect.width = startEditRect.width + moveX;
              editRect.height = startEditRect.height + moveY;
              break;
          }
          draw();
          return;
        }
      }
    };

    this.el.onmouseup = (e) => {
      if (this.readonly) return;
      e.target.style.cursor = "default";
      if (drawingRect) {
        drawingRect = null;
        // 如果绘制的矩形太小，则不添加，防止原地点击时添加矩形
        // 如果反向绘制，则调整为正向
        const width = Math.abs(e.offsetX - startX);
        const height = Math.abs(e.offsetY - startY);
        if (width > 1 || height > 1) {
          const newrect = computeRect({
            x: Math.min(startX, e.offsetX),
            y: Math.min(startY, e.offsetY),
            width,
            height,
            rotatable: true,
            rotateAngle: 0,
          });
          newrect.id = getUUID();
          this.rects.push(newrect);
          this.emit("add", newrect);
          draw();
        }
        return;
      }

      if (draggingRect) {
        draggingRect = null;
        return;
      }
      if (rotatingRect) {
        rotatingRect = null;
        return;
      }
      if (dragingEditor) {
        dragingEditor = null;
        return;
      }
      this.pickRect = null;
    };

    function drawEditor(rect) {
      that.ctx1.save();
      const editor = computeEditRect(rect);
      that.ctx1.fillStyle = that.scolor;
      const { rotl, rotr, ...rects } = editor;

      // 绘制旋转按钮
      if (rect.rotatable) {
        that.ctx1.fillRect(rotr.x, rotr.y, rotr.width, rotr.height);
        that.ctx1.beginPath();
        that.ctx1.moveTo(rotl.x1, rotl.y1);
        that.ctx1.lineTo(rotl.x2, rotl.y2);
        that.ctx1.stroke();
      }

      // 绘制矩形
      for (const r of Object.values(editor)) {
        that.ctx1.fillRect(r.x, r.y, r.width, r.height);
      }
      that.ctx1.restore();
    }

    function draw() {
      that.ctx1.clearRect(0, 0, that.width, that.height);
      that.ctx1.save();
      that.ctx1.lineWidth = that.lineWidth;
      that.ctx1.strokeStyle = that.color;
      that.ctx1.translate(scaleX, scaleY);
      that.ctx1.scale(scale, scale);
      that.ctx1.translate(-scaleX, -scaleY);
      that.ctx1.translate(translateX, translateY);

      that.ctx1.drawImage(img, 0, 0, that.width, that.height);

      that.rects.forEach((r) => {
        that.ctx1.strokeStyle = r.isEditing ? that.moveColor : that.color;
        that.ctx1.save();
        if (r.rotatable) {
          that.ctx1.translate(r.x + r.width / 2, r.y + r.height / 2);
          that.ctx1.rotate((r.rotateAngle * Math.PI) / 180);
          that.ctx1.translate(-(r.x + r.width / 2), -(r.y + r.height / 2));
        }
        that.ctx1.strokeRect(r.x, r.y, r.width, r.height);

        if (r.isEditing) {
          drawEditor(r);
        }

        that.ctx1.restore();
      });

      if (drawingRect) {
        that.ctx1.strokeRect(
          drawingRect.x,
          drawingRect.y,
          drawingRect.width,
          drawingRect.height
        );
      }

      that.ctx1.restore();
    }
    that.draw = draw;
  }
  getRects() {
    return this.rects;
  }
  deleteRect() {
    let index = this.rects.indexOf(this.pickRect);
    if (index > -1) {
      this.emit("delete", this.pickRect);
      this.rects.splice(index, 1);
      this.draw();
    }
  }
  removeRect(rect) {
    let index = this.rects.indexOf(rect);
    if (index > -1) {
      this.rects.splice(index, 1);
      this.draw();
      this.emit("remove", rect);
    }
  }
  dispose() {
    this.rects = [];
    this.el.onmousedown = null;
    this.el.onmousemove = null;
    this.el.onmouseup = null;
  }
  on(name, callback) {
    this.event[name] = callback;
  }
  emit(name, args) {
    this.event[name]?.(args);
  }
}
